package com.github.jarvett.http;

import com.github.jarvett.utils.NetUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import com.github.jarvett.http.annotation.GetMapping;
import com.github.jarvett.http.annotation.PostMapping;
import com.github.jarvett.http.annotation.RestController;
import com.github.jarvett.utils.BashEnvUtils;
import com.github.jarvett.utils.ClassUtils;
import org.smartboot.http.common.enums.HttpMethodEnum;
import org.smartboot.http.server.HttpBootstrap;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Properties;
import java.util.Set;

@Slf4j
public class SmartHttpApplication {

    private static final Properties prop = new Properties();

    @SneakyThrows
    public static void run(Class<?> mainCls, String... args) {
        log.info("SmartHttpApplication init begin...");
        try {
            load(mainCls.getClassLoader().getResourceAsStream("application.properties"));
            String basePackageName = mainCls.getPackage().getName();
            log.debug("扫描基础package:{}下的各种带注解的类", basePackageName);
            //可能是class文件夹下或者jar包内的
            Set<String> classNames = ClassUtils.getClassName(basePackageName, true);
            parseHttpMethodAnnotation(classNames);
            int port = startHttpServer();
            log.info("SmartHttpApplication init done open http://{}:{}", BashEnvUtils.findFirstNonLoopBackAddressIp(), port);
        } catch (RuntimeException re) {
            log.error("SmartHttpApplication init fail ", re);
            System.exit(-1);
        }
    }

    private static void parseHttpMethodAnnotation(Set<String> classNames) throws Exception {
        for (String name : classNames) {
            Class<?> aClass = Class.forName(name);
            //先处理其他通用注解
            processBaseAnnotation(name, aClass);
            processWebAnnotation(name, aClass);
        }
    }

    private static void processBaseAnnotation(String name, Class<?> aClass) {
        log.debug("处理类{}的注解",name);
    }

    private static void processWebAnnotation(String name, Class<?> aClass) throws InstantiationException, IllegalAccessException {
        RestController restController = aClass.getAnnotation(RestController.class);
        if (null != restController) {
            if(restController.skip()){
                return;
            }
            log.debug("解析@RestController类:{},前缀路径:{}", name, restController.value());
            Object controller = aClass.newInstance();
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                GetMapping getMapping = method.getAnnotation(GetMapping.class);
                if (null != getMapping) {
                    checkAndPutUriToMap(combineUri(restController.value(), getMapping.value()), method, controller, HttpMethodEnum.GET);
                }
                PostMapping postMapping = method.getAnnotation(PostMapping.class);
                if (null != postMapping) {
                    checkAndPutUriToMap(combineUri(restController.value(), postMapping.value()), method, controller, HttpMethodEnum.POST);
                }
            }
        }
    }

    private static String combineUri(String cs, String ms) {
        StringBuilder sb = new StringBuilder();
        fixSlash(cs, sb);
        fixSlash(ms, sb);
        return sb.toString();
    }

    private static void fixSlash(String ms, StringBuilder sb) {
        if (ms.startsWith("/")) {
            sb.append(ms);
        } else if(!ms.isEmpty()){
            sb.append("/").append(ms);
        }
        if (ms.endsWith("/")) {
            sb.deleteCharAt(sb.length() - 1);
        }
    }

    private static void checkAndPutUriToMap(String uri, Method method, Object controller, HttpMethodEnum methodEnum) {
        if (SmartHttpContext.uriMethodMap.containsKey(uri)) {
            throw new RuntimeException(MessageFormat.format("该uri:{0}已存在Mapping", uri));
        }
        log.debug("加载{}方法:{},对应：{}", methodEnum.getMethod(), uri, method.toString());
        SmartHttpContext.uriMethodMap.put(uri, method);
        SmartHttpContext.methodObjectMap.put(method, controller);
    }

    private static int startHttpServer() {
        int port = Integer.parseInt(getPropValue("http.port", "8080"));
        port = NetUtil.findAvailablePort(port);
        log.info("准备启动web服务smart-http...");
        HttpBootstrap _server = new HttpBootstrap();
        _server.configuration().bannerEnabled(false);
        SmartHttpContextHandler _handler = new SmartHttpContextHandler();
        _server.httpHandler(_handler);
        _server.setPort(port).start();
        ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
        int activeCount = threadGroup.activeCount();
        Thread[] threads = new Thread[activeCount];
        threadGroup.enumerate(threads);
        boolean hasOkSmartHttpServer = false;
        for (Thread t : threads) {
            if (null != t && null != t.getName() && t.getName().equals("smart-socket:connect")) {
                log.info("当前共有{}个活动线程，检查到main线程组下存在有smart-socket:connect的线程,web服务启动成功", activeCount);
                hasOkSmartHttpServer = true;
            }
        }
        if (!hasOkSmartHttpServer) {
            throw new RuntimeException("无法检测到smart-http服务的smart-socket:connect线程,请查看日志");
        }
        return port;
    }

    private static void load(InputStream inStream) throws IOException {
        if(null == inStream) return;
        prop.load(inStream);
        log.info("读取解析配置文件开始...");
        prop.forEach((k, v) -> {
            v = BashEnvUtils.parseTemplate(v.toString());
            prop.put(k, v);
            log.info(String.format("%-45s", k) + "" + v);
        });
        log.info("读取解析配置文件结束!!!");
    }

    public static String getPropValue(String key) {
        return prop.getProperty(key);
    }

    public static String getPropValue(String key, String defValue) {
        return prop.getProperty(key, defValue);
    }

}
